#include "mpHttpRequestMultipartContentImpl.h"
#include "mpHashUtils.h"

#include "mpFileUtils.h"
#include "mpHttpException.h"
#include "mpLog.h"

namespace MP{
namespace HTTP{
namespace INTERNAL{

HttpRequestMultipartContentImpl::HttpRequestMultipartContentImpl(const std::string & boundary)
    :_parts(),_files(){

    if(_boundary.empty()){
        _boundary = "----WebKitFormBoundary7TMYhSONfkAM2z3a";
    }

    std::string tmp = "multipart/form-data;boundary=" + _boundary;
    HttpRequestContent::setContentType(tmp);
}

const std::string& HttpRequestMultipartContentImpl::getBoundary() const{
    return _boundary;
}

void HttpRequestMultipartContentImpl::addFormData(const std::string & key,
                                                  const std::string & val){

    if (key.empty() || val.empty()) {
        return;
    }

    std::vector<HttpFormData>::iterator tmp;
    tmp = std::find(_parts.begin(), _parts.end(), key);

    if( tmp != _parts.end() ){
        tmp->value = val;
    }
    else{
        HttpFormData formData(key,val);
        _parts.emplace_back(formData);
    }
    _sendContentLength = 0;
}


void HttpRequestMultipartContentImpl::clearFormDatas() {
    _parts.clear();
    _sendContentLength = 0;
}

void HttpRequestMultipartContentImpl::addFormFile(const std::string & name,
                                                  const std::string & fileName,
                                                  const std::string & filePath){

    if (name.empty() || fileName.empty() || filePath.empty()) {
        return;
    }

    std::vector<HttpFormFile>::iterator tmp;
    tmp = std::find(_files.begin(), _files.end(), name);

    if( tmp != _files.end() ){
        tmp->fileName = fileName;
        tmp->filePath = filePath;
    }
    else{
        HttpFormFile formFile(name,fileName,filePath);
        _files.emplace_back(formFile);
    }

    _sendContentLength = 0;
}

void HttpRequestMultipartContentImpl::clearFormFiles() {
    _files.clear();
    _sendContentLength = 0;
}

void HttpRequestMultipartContentImpl::setContentType(const std::string & content_type) {
//    std::string tmp = content_type + ";boundary=" + _boundary;
//    HttpRequestContent::setContentType(tmp);

    //_httpcontent_type = content_type;
}

std::string HttpRequestMultipartContentImpl::getMd5(){
    return {};
}

std::size_t HttpRequestMultipartContentImpl::getContentLength() {

    if(_sendContentLength <= 0){

        if(_parts.size() != 0 || _files.size() != 0){

            size_t result = 0;
            size_t size_boundary = _boundary.length();

            for(auto it = _parts.begin();it != _parts.end();++it){
                //addBoundary(steam);
                result += (size_boundary + 4);

                std::string strFormData;
                buildFormData(it->name,it->value,strFormData);

                result += strFormData.size();

            }

            for(auto it = _files.begin();it != _files.end();++it){
                //addBoundary(steam);
                result += (size_boundary + 4);

                std::string strFileDataWithOutFileSteam;
                buildFileDataWithOutFileSteam(it->name,it->fileName,strFileDataWithOutFileSteam);

                result += strFileDataWithOutFileSteam.size();
                //writeFile(it->filePath,steam);
                result += (MP::FileUtils::get_file_size(it->filePath) + 2);

            }

            //fillEnd
            result += (size_boundary + 6);
            _sendContentLength = result;
        }
    }

    return _sendContentLength;
}

//（如果返回Fasle表示数据存在异常）
bool HttpRequestMultipartContentImpl::beginWriteData() {

    _isPartWrite = false;                       //parts是否已发送
    _file_index = 0;                            //待发送的文件索引
    _un_read_size=0;

    _buf_string.clear();

    memset(_buf_file,'\0',MP::HTTP::READ_FILE_BUF_SIZE);

    if(_read_file.is_open()){
        _read_file.close();
    }

    bool result = true;
    for(auto it = _files.begin();it != _files.end();++it){
        if(!MP::FileUtils::check_file_isexist(it->filePath)){
            MPLOG_ERROR << "HttpRequestMultipartContentImpl : the file [ " << it->filePath << " ] does not exist";
            result = false;
            break;
        }
    }
    return result;
}

void  HttpRequestMultipartContentImpl::endWriteData(){
    _isPartWrite = false;                   //parts是否已发送
    _file_index = 0;                        //待发送的文件索引
    _un_read_size=0;

    _buf_string.clear();

    memset(_buf_file,'\0',MP::HTTP::READ_FILE_BUF_SIZE);

    if(_read_file.is_open()){
        _read_file.close();
    }
}

//如果返回真，则表示仍有数据需要写入；如果返回假，则表示当前数据写入完成后则Payload写入完毕
bool HttpRequestMultipartContentImpl::onWriteDataSome(std::vector<boost::asio::const_buffer> & buffers){

    _buf_string.clear();

    //首先，一次性获取Part相关内容
    if(!_isPartWrite && _parts.size() > 0){
        _isPartWrite = true;
        fillPartContentData(buffers);
        return true;
    }

    //其次，逐项获取File相关内容
    if(_files.size() > 0){

        //如果_read_file为假，则表示需要开启一个新文件的读取
        if(!_read_file.is_open()){

            if( _file_index < _files.size())
            {
                //获取下一个新文件的信息，并将文件读取索引+1
                auto it = _files.at(_file_index);
                _file_index += 1;

                //打开文件
                _read_file.open(it.filePath, std::ios::in | std::ios::binary);
                if(!_read_file.is_open()){
                    //如果文件打开失败，则直接退出（按照HttpDispatcher的逻辑，程序将取消当前请求）
                    MPLOG_ERROR << "HttpRequestMultipartContentImpl : the file [ " << it.filePath << " ] open fail";
                    return false;
                }
                else{

                    //如果文件打开成功，计算文件大小，并将文件Header写入Buffer，最后返回真
                    _read_file.seekg(0, std::ios::end);
                    _un_read_size = _read_file.tellg();

                    _read_file.seekg(0);

                    _buf_string.clear();
                    addBoundary(_buf_string);
                    buildFileDataWithOutFileSteam(it.name,it.fileName,_buf_string);
                    buffers.push_back(boost::asio::buffer(_buf_string));

                    return true;
                }
            }
        }
        else{

            if(_read_file.good()){


                std::streamsize size = _un_read_size > MP::HTTP::READ_FILE_BUF_SIZE ? MP::HTTP::READ_FILE_BUF_SIZE : _un_read_size;
                _read_file.read(_buf_file,size);

                if(_read_file.gcount() == 0){
                    //如果读取出来的数据为空，则返回假（按照HttpDispatcher的逻辑，程序将取消当前请求）
                    MPLOG_ERROR << "HttpRequestMultipartContentImpl read file empty. ";
                    return false;
                }

                _un_read_size = _un_read_size - _read_file.gcount();
                buffers.push_back(boost::asio::buffer(_buf_file,_read_file.gcount()));

                if(_un_read_size == 0){
                    //如果文件读取完,那么关闭文件，并添加换行符
                    _read_file.close();
                    fillCrlf(buffers);
                }

                return true;
            }
            else{
                //如果文件读取完,那么关闭文件，并添加换行符
                _read_file.close();
                fillCrlf(buffers);
                return true;
            }

        }

    }

    //如果都完成，则写入结束符并返回false
    fillEnd(buffers);
    return false;
}

void HttpRequestMultipartContentImpl::fillPartContentData(std::vector<boost::asio::const_buffer> & buffers){

    if(!_buf_string.empty()){
        throw HttpException("func buf_string can only be used once in a function onWriteDataSome");
    }

    for(auto it = _parts.begin();it != _parts.end();++it){
        addBoundary(_buf_string);
        buildFormData(it->name,it->value,_buf_string);
    }
    buffers.push_back(boost::asio::buffer(_buf_string));
}


void HttpRequestMultipartContentImpl::fillCrlf(std::vector<boost::asio::const_buffer> & buffers){

    if(!_buf_string.empty()){
        throw HttpException("func buf_string can only be used once in a function onWriteDataSome");
    }

    _buf_string.append(MP::HTTP::CRLF);
    buffers.push_back(boost::asio::buffer(_buf_string));
}

void HttpRequestMultipartContentImpl::fillEnd(std::vector<boost::asio::const_buffer> & buffers){

    if(!_buf_string.empty()){
        throw HttpException("func buf_string can only be used once in a function onWriteDataSome");
    }

    _buf_string.append(MP::HTTP::DOUBLE_HYPHEN);
    _buf_string.append(_boundary);
    _buf_string.append(MP::HTTP::DOUBLE_HYPHEN);
    _buf_string.append(MP::HTTP::CRLF);

    buffers.push_back(boost::asio::buffer(_buf_string));
}

void HttpRequestMultipartContentImpl::addBoundary(std::string & str)  {

    str.append(MP::HTTP::DOUBLE_HYPHEN);
    str.append(_boundary);
    str.append(MP::HTTP::CRLF);

    //    steam << MP::HTTP::DOUBLE_HYPHEN;
    //    steam << _boundary;
    //    steam << MP::HTTP::CRLF;
}

void HttpRequestMultipartContentImpl::buildFormData(const std::string & name,
                                                    const std::string & value,
                                                    std::string & str){

    str.append("Content-Disposition: form-data;name=\"");
    str.append(name);
    str.append("\"");
    str.append(MP::HTTP::CRLF);

    str.append("Content-Type: text/plain; charset=UTF-8");
    str.append(MP::HTTP::CRLF);
    str.append(MP::HTTP::CRLF);

    str.append(value);
    str.append(MP::HTTP::CRLF);

    //        strForm += ("Content-Disposition: form-data;name=\"" + node.first + "\"\r\n");
    //        strForm += "Content-Type: text/plain; charset=UTF-8\r\n\r\n";
    //        strForm += (node.second + "\r\n");
}

void HttpRequestMultipartContentImpl::buildFileDataWithOutFileSteam(const std::string & name,
                                                                    const std::string & fileName,
                                                                    std::string & str){

    str.append("Content-Disposition: form-data;name=\"");
    str.append(name);
    str.append("\"; filename=\"");
    str.append(fileName);
    str.append("\"");
    str.append(MP::HTTP::CRLF);

    str.append("Content-Type: application/octet-stream");
    str.append(MP::HTTP::CRLF);

    str.append("Content-Transfer-Encoding: binary");
    str.append(MP::HTTP::CRLF);

    str.append(MP::HTTP::CRLF);

    //    strFile += ("Content-Disposition: form-data; name=\""+ node.first +"\"; filename=\"" + node.first + ".png\"\r\n");
    //    strFile += "Content-Type: application/octet-stream\r\n";
    //    strFile += "Content-Transfer-Encoding: binary\r\n\r\n";

    //    m_sendFileStream.seekg(0, m_sendFileStream.end);
    //    int length = (int)m_sendFileStream.tellg();
    //    m_sendFileStream.seekg(0, m_sendFileStream.beg);

    //    std::string buffer(10240, '\0');    //10KB
    //    int offset = 0;
    //    int readSize = std::min(10240, length - offset);

    //    while (readSize > 0 && m_sendFileStream.read(&buffer[0], readSize)) {
    //        strFile += buffer;
    //        offset += readSize;

    //        readSize = std::min(10240, length - offset);
    //    }

    //    strFile += "\r\n";
}


//void HttpRequestMultipartContentImpl::writeFile(const std::string & filePath,
//                                            std::ostream & steam){


//    std::ifstream file(filePath, std::ios::in | std::ios::binary);
//    if (!file){
//        return;
//    }

//    //    // 计算文件大小
//    //    file.seekg(0, ios::end);
//    //    size_t size = file.tellg();
//    //    file.seekg(0);

//    std::streamsize buf_size = 16384;       //16 * 1024;
//    char buf[buf_size];

//    file.seekg(0,std::ios::beg);
//    while (file.good()) {

//        file.read(buf, buf_size);
//        //steam << buf;
//        steam.write(buf,file.gcount());
//        steam.flush();
//    }

//    file.close();


//    steam << MP::HTTP::CRLF;
//}


//void HttpRequestMultipartContentImpl::onWrite(std::ostream & steam)  {
//    //    std::string _boundary;
//    //    std::vector<HttpFormData> _parts;
//    //    std::vector<HttpFormFile> _files;
//    if(_parts.size() == 0 && _files.size() == 0){
//        return;
//    }

//    for(auto it = _parts.begin();it != _parts.end();++it){

//        addBoundary(steam);

//        std::string strFormData;
//        buildFormData(it->name,it->value,strFormData);

//        steam << strFormData;
//    }


//    for(auto it = _files.begin();it != _files.end();++it){

//        addBoundary(steam);

//        std::string strFileDataWithOutFileSteam;
//        buildFileDataWithOutFileSteam(it->name,it->fileName,strFileDataWithOutFileSteam);

//        steam << strFileDataWithOutFileSteam;

//        writeFile(it->filePath,steam);
//    }

//    endBoundary(steam);
//}



}   //INTERNAL
}   //HTTP
}   //MP
